package java_thinking.unit_18_IO;

import java.io.*;

/**
 * @Desc:
 *      1.基本被装饰对象（对应于输入字节流的媒体字节流）
 *      2.ByteArrayInputStream:采用适配器设计模式,ByteArray到ByteArrayInputStream的适配
 *
 * @author: cww
 * @DateTime: 2019-05-19 22:17
 */

public class ByteArrayInputStream extends InputStream {
    /**
     * 内部的buffer，一般通过构造器输入
     */
    protected byte[] buf;
    /**
     * 当前位置的cursor。从0至byte数组的长度。
     * byte[pos]就是read方法读取的字节
     */
    protected int pos;

    /**
     * mark的位置。
     */
    protected int mark = 0;

    /**
     * 流中字节的数目。
     */
    protected int count;


    /**
     * 从一个byte[]创建一个ByteArrayInputStream
     * @param buf
     */
    public ByteArrayInputStream(byte buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }

    public ByteArrayInputStream(byte buf[], int offset, int length) {
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.mark = offset;
    }

    /**
     * 从流中读取下一个字节
     * @return
     */
    @Override
    public synchronized int read() {
        /**
         *  返回下一个位置的字节
         *  流中没有数据则返回-1
         */
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }

    /**
     * 下面这个方法很有意思！从InputStream中可以看出其提供了该方法的实现。
     * 为什么ByteArrayInputStream要覆盖此方法呢？
     * 同样的我们在Java Collections Framework中可以看到：
     * AbstractCollection利用iterator实现了Collecion接口的很多方法。但是，
     * 在ArrayList中却有很多被子类覆盖了。为什么如此呢:
     *      出于速度的原因！他们都用到System.arraycopy方法。想想为什么
     *      某些时候，父类不能完全实现子类的功能，父类的实现一般比较通用。
     *      当子类有更有效的方法时，我们会覆盖这些方法。
     * @param b
     * @param off
     * @param len
     * @return
     */
    @Override
    public synchronized int read(byte b[], int off, int len) {
        /**
         * 首先检查输入参数的状态是否正确
         */
        if (b == null) {

            throw new NullPointerException();

        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        }

        if (pos >= count) {
            return -1;
        }

        if (pos + len > count) {
            len = count - pos;
        }

        if (len <= 0) {
            return 0;
        }

        /**
         * java中提供数据复制的方法
         */
        System.arraycopy(buf, pos, b, off, len);
        pos += len;
        return len;
    }

    /**
     * 下面这个方法，在InputStream中也已经实现了。
     * 但是当时是通过将字节读入一个buffer中实现的，好像效率低了一点。
     * 看看下面这段代码，是否极其简单呢？！
     * @param n
     * @return
     */
    @Override
    public synchronized long skip(long n) {

        /**
         * 当前位置，可以跳跃的字节数目
         */

        if (pos + n > count) {
            n = count - pos;
        }
        //小于0，则不可以跳跃
        if (n < 0) {
            return 0;
        }
        //跳跃后，当前位置变化
        pos += n;
        return n;

    }

    /**
     * 比InputStream中的方法简单、高效吧！
     * @return
     */
    @Override
    public synchronized int available() {
        return count - pos;

    }

    /**
     * 查询流中还有多少字节没有读取。
     * 在我们的ByteArrayInputStream中就是当前位置以后字节的数目。
     * @return
     */
    @Override
    public boolean markSupported() {
        return true;
    }

    /**
     * ByteArrayInputStream支持mark所以返回true
     * @param readAheadLimit
     */
    @Override
    public void mark(int readAheadLimit) {
        mark = pos;
    }

    /**
     * 在流中当前位置mark。
     * 在我们的ByteArrayInputStream中就是将当前位置赋给mark变量。
     * 读取流中的字节就是读取字节数组中当前位置向后的的字节。
     */
    @Override
    public synchronized void reset() {
        pos = mark;
    }

    /**
     * 重置流。即回到mark的位置。
     * @throws IOException
     */

    @Override
    public void close() throws IOException {   }

}

class ByteDemo {
    static String fileLocation = "src/java/maindemo/java_thinking/unit_18_IO/ByteArrayInputStream.java";
    public static void main(String[] args) throws IOException {
        //内存中的一个字节数组
        byte[] buf = "11111".getBytes();
        //创建该字节数组的输入流
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buf);
        //内存中的另一个数组
        byte[] pos = new byte[buf.length];
        //通过字节数组输入流向该内存中输入字节
        while (byteArrayInputStream.read(pos)!=-1);
        byteArrayInputStream.close();
        System.out.println(new String(pos));

        OutputStream out = new ByteArrayOutputStream();
        out.write(buf);

    }
}
